using System;
using System.Collections.Generic;
using System.Text;
using System.Web.Services.Description;
using System.Reflection;
using System.Web.Services.Discovery;
using System.IO;
using System.Net;
using System.Xml;
using System.CodeDom;
using System.Xml.Schema;
using System.CodeDom.Compiler;
using Microsoft.CSharp;

namespace GetTogether.Studio.WebService
{
    public class WsdlXml
    {
        private static void CheckForImports(ServiceDescriptionImporter svcDescImporter, string wsdlAddress)
        {
            DiscoveryClientProtocol dcp = new DiscoveryClientProtocol();
            dcp.DiscoverAny(wsdlAddress);
            dcp.ResolveAll();
            foreach (object osd in dcp.Documents.Values)
            {
                if (osd is ServiceDescription) svcDescImporter.AddServiceDescription((ServiceDescription)osd, null, null);
                if (osd is XmlSchema) svcDescImporter.Schemas.Add((XmlSchema)osd);
            }
        }

        private static string GetWsdlInformation(string wsdlAddress)
        {
            WebRequest req = WebRequest.Create(wsdlAddress);
            WebResponse result = req.GetResponse();
            Stream ReceiveStream = result.GetResponseStream();
            Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
            StreamReader sr = new StreamReader(ReceiveStream, encode);
            string wsdlSource = sr.ReadToEnd();
            sr.Close();
            return wsdlSource;
        }

        public static Extension.SoapHttpClientProtocolExtension GetObject(Assembly ass)
        {
            Extension.SoapHttpClientProtocolExtension proxyInstance = (Extension.SoapHttpClientProtocolExtension)CreateInstance("", ass);
            return proxyInstance;
        }

        public static object CreateInstance(string objTypeName, Assembly ass)
        {
            if (objTypeName == "" || objTypeName == null)
            {
                foreach (Type ty in ass.GetTypes())
                {
                    if (ty.BaseType == typeof(Extension.SoapHttpClientProtocolExtension))
                    {
                        objTypeName = ty.Name;
                        break;
                    }
                }
            }
            Type t = ass.GetType("GetTogether.Studio.WebService.Extension.DynamicProxy." + objTypeName);
            return Activator.CreateInstance(t);
        }

        public static Assembly BuildAssemblyFromWsdl(string wsdlUrl)
        {
            string wsdlContent = GetWsdlInformation(wsdlUrl);
            StringReader wsdlStringReader = new StringReader(wsdlContent);
            XmlTextReader tr = new XmlTextReader(wsdlStringReader);
            ServiceDescription sd = ServiceDescription.Read(tr);
            tr.Close();

            CodeNamespace cns = new CodeNamespace("GetTogether.Studio.WebService.Extension.DynamicProxy");
            ServiceDescriptionImporter sdi = new ServiceDescriptionImporter();
            CheckForImports(sdi, wsdlUrl);
            sdi.ProtocolName = "Soap";
            sdi.Import(cns, null);

            CodeTypeDeclaration ctDecl = cns.Types[0];
            cns.Types.Remove(ctDecl);
            ctDecl.BaseTypes[0] = new CodeTypeReference("GetTogether.Studio.WebService.Extension.SoapHttpClientProtocolExtension");
            cns.Types.Add(ctDecl);

            CSharpCodeProvider cscp = new CSharpCodeProvider();
            ICodeGenerator icg = cscp.CreateGenerator();
            StringBuilder srcStringBuilder = new StringBuilder();
            StringWriter sw = new StringWriter(srcStringBuilder);
            icg.GenerateCodeFromNamespace(cns, sw, null);
            string proxySource = srcStringBuilder.ToString();
            sw.Close();

            CompilerParameters cp = new CompilerParameters();
            cp.ReferencedAssemblies.Add("System.dll");
            cp.ReferencedAssemblies.Add("System.Xml.dll");
            cp.ReferencedAssemblies.Add("System.Web.Services.dll");
            cp.ReferencedAssemblies.Add("System.Data.dll");
            cp.ReferencedAssemblies.Add(System.Reflection.Assembly.
                GetExecutingAssembly().Location);

            cp.GenerateExecutable = false;
            cp.GenerateInMemory = false;
            cp.IncludeDebugInformation = false;

            ICodeCompiler icc = cscp.CreateCompiler();
            CompilerResults cr = icc.CompileAssemblyFromSource(cp, proxySource);

            if (cr.Errors.Count > 0)
                throw new Exception(string.Format(@"Build failed: {0} errors", cr.Errors.Count));

            return cr.CompiledAssembly;
        }
    }
}
